package Or

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"db2s/parDef"
	"db2s/ref"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

type (
	Conn struct {
		ConText         *atomic.Value `json:"conText"`
		mu              *sync.Mutex
		Mode            *atomic.Value                `json:"mode"`
		Input           global.StartPartConfigInputP `json:"input"`
		parameterModify []ModifyParameter
		newNode         *atomic.Value
	}
	ConText struct {
		Device   string        `json:"device"`
		Jdbc     string        `json:"jdbc"`
		MaxIdle  int           `json:"maxIdle"`
		MaxOpen  int           `json:"maxOpen"`
		IdleTime time.Duration `json:"idleTime"`
		LeftTime time.Duration `json:"leftTime"`
	}
	ModifyParameter struct {
		Time          string  `json:"timestamp"`
		Mode          string  `json:"mode"`
		Object        string  `json:"object"`
		Seq           string  `json:"seq"`
		ShardName     string  `json:"shardName"`
		ParameterName string  `json:"parameter"`
		OldValue      string  `json:"oldValue"`
		NewValue      string  `json:"newValue"`
		Status        bool    `json:"status"`
		Con           ConText `json:"con"`
	}
	ConDbQuery struct {
		DB        *sql.DB
		ShardName string
		Input     global.StartPartConfigInputP
	}
	nodeConn struct {
		db        *sql.DB
		max       int
		shardName string
		Parameter []ModifyParameter
	}
)

func NewConnObject() Conn {
	var node, mode, context atomic.Value
	var mu sync.Mutex
	return Conn{
		ConText:         &context,
		Mode:            &mode,
		mu:              &mu,
		parameterModify: []ModifyParameter{},
		newNode:         &node,
	}
}
func syncP() map[string] /*parameter type*/ any /*parameter info*/ {
	return map[string]any{"static": rowsStaticParameter, "dynamic": rowsDynamicParameter}
}
func StructP() map[string] /*parameter type*/ any /*parameter info*/ {
	return map[string]any{"static": metaStaticParameter, "dynamic": metaDynamicParameter}
}
func modifyP(mode string) (returnValue map[string]any) {
	Func, ok := modeP[mode]
	if !ok {
		return
	}
	if Func.Kind() != reflect.Func { // 确保 Func 是一个函数
		return
	}
	returnValue = Func.Call(nil)[0].Interface().(map[string]any)
	return
}

func Query(s ConDbQuery, pName string) (result string, err error) {
	var event = "[Query]"
	var f1 any
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf(event, errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	querySql := fmt.Sprintf("select VARIABLE_VALUE from performance_schema.global_variables where VARIABLE_NAME='%v'", pName)
	if _, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: querySql,
		Expect:       ETL.String,
	}); err != nil {
		err = ref.ErrAddPrintf(event, err)
		return
	}
	if f1 == nil || len(f1.(string)) == 0 {
		return
	}
	result = f1.(string)
	return
}

func Set(s ConDbQuery, pName string, pValue any) (err error) {
	var event = "[Set]"
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf(event, errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	setSql := fmt.Sprintf("set global %s = %v", pName, pValue)
	if _, err = ExecUpdateSql(ExecSqlInputS{
		Ss:           s,
		UpdateSqlStr: []string{setSql},
	}); err != nil {
		err = ref.ErrAddPrintf(event, err)
		return
	}
	return
}

func (pool Conn) modify(s ConDbQuery, timeStamp string, pInfo map[string]any) (result []ModifyParameter) {
	var b int
	var err error
	for variablesName, expect := range pInfo {
		b++
		expectValue := fmt.Sprintf("%v", expect)
		res := ModifyParameter{
			Object:        "variables",
			Time:          timeStamp,
			Mode:          fmt.Sprintf("%v", pool.Mode.Load()),
			Seq:           strconv.Itoa(b),
			ShardName:     s.ShardName,
			ParameterName: variablesName,
			NewValue:      expectValue,
		}
		if res.OldValue, err = Query(s, variablesName); err != nil {
			continue
		}
		if strings.EqualFold(expectValue, res.OldValue) {
			continue
		}
		if Set(s, variablesName, res.NewValue) != nil {
			continue
		}
		res.Status = true
		res.Con = pool.ConText.Load().(ConText)
		result = append(result, res)
	}
	return
}
func (pool Conn) parameter(node nodeConn) (res []ModifyParameter) {
	timeStamp := time.Now().Format("2006-01-02T15:04:05")
	pInfo, ok := modifyP(fmt.Sprintf("%v", pool.Mode.Load()))["dynamic"]
	if !ok {
		return
	}
	s := ConDbQuery{
		DB:        node.db,
		ShardName: node.shardName,
		Input:     pool.Input,
	}
	return pool.modify(s, timeStamp, pInfo.(map[string]any))
}
func characterQ(s global.Conn) (res string, err error) {
	var (
		db    *sql.DB
		f1    any
		event = "[characterQ]"
	)
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf(event, errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	if db, err = sql.Open(s.Device, s.Jdbc); err != nil {
		return
	}
	r := global.DBGetCharacterS{
		DB: map[string]*sql.DB{"single": db},
		Input: global.StartPartConfigInputP{
			SqlMode: true,
		},
	}
	strSql := fmt.Sprintf("SELECT value AS \"character\" FROM nls_database_parameters WHERE parameter LIKE 'NLS_CHARACTERSET'")
	if _, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           r,
		Event:        event,
		SelectSqlStr: strSql,
		Expect:       ETL.String,
	}); err != nil || f1 == nil {
		return
	}
	res = f1.(string)
	return
}
func jdbcSpl1(s ConText) (res string) {
	res = s.Jdbc
	//"oracle://SYSDBA:SYSDBA@172.17.120.106:52360?autoCommit=true"
	return
}

func jdbcExpandFunc1(s ConText) (JdbcExpandValue []string) {
	//var (
	//	charset string
	//	err     error
	//)
	//if !strings.Contains(s.Jdbc, "charset=") {
	//	if charset, err = getNlsCharacterSet(s); err == nil {
	//		JdbcExpandValue = append(JdbcExpandValue, fmt.Sprintf("charset=%v", charset))
	//	}
	//}
	//if !strings.Contains(s.Jdbc, "noTimezoneCheck=") {
	//	JdbcExpandValue = append(JdbcExpandValue, fmt.Sprintf("noTimezoneCheck=1"))
	//}
	return
}
func maxUseConnSum(s ConDbQuery) (maxAvailableConnection int, err error) {
	var (
		event  = "[maxConnectionSum]"
		result global.Return
	)
	defer func() {
		if r := ref.RecoverPanic(event, recover()); err == nil && r != nil {
			err = r
		}
	}()
	if result, err = ETL.QuerySql(context.Background(), ETL.ReadOpt{
		DB: s.DB,
	}, QueryColValManage, nil, []string{getMaxConnections(maxConnection[0], "", "")}, ETL.String); err != nil {
		return 0, err
	}
	if result.Result == nil || len(result.Result.(string)) == 0 {
		return
	}
	if maxAvailableConnection, err = strconv.Atoi(fmt.Sprintf("%v", result.Result)); err != nil {
		err = ref.ErrAddPrintf(event, err)
		return
	}
	return
}

/*
连接数据库，返回连接内存地址
*/
func open(s ConText, shardName string) (res nodeConn, err error) {
	var event = "[open]"
	defer func() {
		if r := ref.RecoverPanic(event, recover()); err == nil && r != nil {
			err = r
		}
	}()
	res.shardName = shardName
	//s.JdbcExpandValue = jdbcExpandFunc(s)
	if res.db, err = sql.Open(s.Device, jdbcSpl1(s)); err != nil {
		err = errors.New(fmt.Sprintf("database open fail. case: %v", err))
		return
	}
	if err = res.db.Ping(); err != nil {
		err = errors.New(fmt.Sprintf("database connection fail. case: %v", err))
		return
	}
	res.db.SetConnMaxIdleTime(120 * time.Second)
	res.db.SetConnMaxLifetime(3600 * time.Second)
	res.db.SetMaxIdleConns(30)
	if res.max, err = maxUseConnSum(ConDbQuery{DB: res.db, ShardName: shardName, Input: global.StartPartConfigInputP{SqlMode: true}}); err != nil {
		err = ref.ErrAddPrintf(event, err)
		return
	}
	if res.max == 0 {
		res.max = 100
	}
	res.db.SetMaxOpenConns(res.max / 2)
	return
}
func (pool Conn) Init() {

}
func (pool Conn) Optimize() (err error) {
	var res nodeConn
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf("Oracle.Optimize", errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	if res, err = open(pool.ConText.Load().(ConText), "single"); err != nil {
		err = ref.ErrAddPrintf("Oracle.Optimize", err)
		return
	} else {
		pool.parameterModify = append(pool.parameterModify, pool.parameter(res)...)
	}
	return pool.Close()
}
func (pool Conn) Open() (err error) {
	var res nodeConn
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf("Oracle.Open", errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	if res, err = open(pool.ConText.Load().(ConText), "single"); err != nil {
		err = ref.ErrAddPrintf("Oracle.Open", err)
		return
	}
	switch newNode := pool.newNode.Load(); newNode.(type) {
	case []nodeConn:
		newNode1 := newNode.([]nodeConn)
		newNode1 = append(newNode1, res)
		pool.newNode.Swap(newNode1)
	default:
		newNode1 := []nodeConn{res}
		pool.newNode.Swap(newNode1)
	}
	return
}
func (pool Conn) Close() (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf("Oracle.Close", errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	switch newNode := pool.newNode.Load(); newNode.(type) {
	case []nodeConn:
		for _, node := range newNode.([]nodeConn) {
			if err = node.db.Close(); err != nil {
				err = ref.ErrAddPrintf("Oracle.Close", err)
				return
			}
		}
	}
	return
}
func (pool Conn) GetFreeConn(object string) int64 {
	return 0
}

func (pool Conn) pingContext(node *sql.DB) (db *sql.DB) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel() // 确保在函数结束时取消上下文
	for {
		if err := node.PingContext(ctx); err == nil {
			return node
		}
		// 检查上下文是否超时
		select {
		case <-ctx.Done():
			// 超时处理
			return nil
		default:
			// 继续尝试 ping
			time.Sleep(100 * time.Millisecond) // 可选：添加延迟以避免过于频繁的 ping
		}
	}
}
func (pool Conn) GetDB(object string) *sql.DB {
	pool.mu.Lock()
	defer pool.mu.Unlock()
	if !strings.EqualFold(object, parDef.Single) {
		object = parDef.Single
	}
	switch newNode := pool.newNode.Load(); newNode.(type) {
	case []nodeConn:
		for _, node := range newNode.([]nodeConn) {
			if strings.EqualFold(node.shardName, object) {
				if pool.pingContext(node.db) != nil {
					return node.db
				} else {
					return nil
				}
			}
		}
	}
	return nil
}
func (pool Conn) Set(parameter string, value any) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf("Oracle.Set", errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	switch parameter {
	case "mode":
		pool.Mode.Store(value)
	case "conText":
		var p ConText
		if err = json.Unmarshal(value.([]byte), &p); err != nil {
			err = ref.ErrAddPrintf("Oracle.Set", err)
			return
		}
		pool.ConText.Store(p)
	}

	return
}
func (pool Conn) GetUse() {

}
func (pool Conn) GetOptimizeRecord() (js [][]byte, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = ref.ErrAddPrintf("Oracle.GetOptimizeRecord", errors.New(fmt.Sprintf("defer recover An exception was captured. abnormal is %v ", r)))
		}
	}()
	for _, v := range pool.parameterModify {
		var s []byte
		if s, err = json.Marshal(v); err != nil {
			err = ref.ErrAddPrintf("Oracle.GetOptimizeRecord", err)
			return nil, err
		}
		js = append(js, s)
	}
	return
}
func (pool Conn) Check(object string) int64 {
	return 0
}
